﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Taype.Trading.BTC.MtGoxAPIClient
{
    /// <summary>
    /// Some useful helper methods
    /// </summary>
    public class MtGoxHelpers
    {
        /// <summary>
        /// The fee on BTC sold is: Fee Amount = (Number of BTC) * (Purchase Price) * (Fee Rate)
        /// </summary>
        /// <param name="amount">The number of BTC purchased</param>
        /// <param name="feeRate">The fee rate as percentage for the account</param>
        /// <param name="price">The price is USD per Bitcoin</param>
        /// <returns>Fee amount in USD</returns>
        public static double CalculateBTCSaleFee(double amount, double price, double feeRate)
        {
            double feeAmount = amount * price * (feeRate / 100);
            return Math.Round(feeAmount, 5);
        }

        /// <summary>
        /// The fee on BTC purchased is: Fee Amount = (Number of BTC) * (Fee rate)
        /// </summary>
        /// <param name="amount">The number of BTC purchased</param>
        /// <param name="feeRate">The fee rate as percentage for the account</param>
        /// <returns>Fee amount in BTC</returns>
        public static double CalculateBTCPurchaseFee(double amount, double feeRate)
        {
            double feeAmount = amount * (feeRate / 100);
            return Math.Round(feeAmount, 8);
        }

        
        /// <summary>
        /// A helper method to determine the effect a given transaction will have on account balances
        /// </summary>
        /// <param name="initialBalanceUSD">Initial account balance in USD</param>
        /// <param name="initialBalanceBTC">Initial account balance in BTC</param>
        /// <param name="amount">Amount of BTC to purchase/sell</param>
        /// <param name="price">Price of BTC for transaction</param>
        /// <param name="feeRate">Fee rate (%) for account</param>
        /// <param name="tradeType">Bid/Ask AKA Buy/Sell</param>
        /// <param name="balanceBTC">Balance of BTC in account after transaction</param>
        /// <param name="balanceUSD">Balance of USD in account after transaction</param>
        /// <param name="feePaid">Fee paid on transaction (amount reported in BTC for Bids/Buys and USD for Asks/Sales)</param>
        public static void CalculateAccountBalancesAfterTrade(double initialBalanceUSD, double initialBalanceBTC, double amount, double price, double feeRate, MtGoxTradeType tradeType, out double balanceBTC, out double balanceUSD, out double feePaid)
        {
            balanceBTC = 0;
            balanceUSD = 0;
            feePaid = 0;
            switch (tradeType)
            { 
                case MtGoxTradeType.ask:
                    // we are selling BTC, fee will be on USD proceeds
                    balanceBTC = Math.Round((initialBalanceBTC - amount), 8);
                    feePaid = Math.Round(CalculateBTCSaleFee(amount, price, feeRate), 5);
                    balanceUSD = Math.Round((initialBalanceUSD + (price * amount) - feePaid), 5);
                    break;
                case MtGoxTradeType.bid:
                    // we are buying BTC, fee will be on BTC reeived
                    balanceUSD = Math.Round(initialBalanceUSD - (price * amount), 5);
                    feePaid = Math.Round(CalculateBTCPurchaseFee(amount, feeRate), 8);
                    balanceBTC = Math.Round((initialBalanceBTC + amount - feePaid), 8);
                    break;
            }
        }

        /// <summary>
        /// Helper method to calculate how many BTC can be purchased at a given dollar, price and fee amount
        /// </summary>
        /// <param name="usdAvailable">Amount of USD available for purchasing BTC</param>
        /// <param name="price">Price of BTC (USD)</param>
        /// <param name="feeRate">Fee rate (%)</param>
        /// <returns>Amount of BTC (after fees in BTC) that will be purchased as a result</returns>
        public static double HowManyBTCCanIGet(double usdAvailable, double price, double feeRate)
        {
            // TODO: There is a rounding error in here somewhere... results seem to be good to 6 significant digits...
            double howManyBTC = (usdAvailable / price) - (usdAvailable / price) * (feeRate / 100);
            // now it is very important to remember that the smallest denomination of BTC that can be bought at MtGox is 0.01 BTC!!
            if (howManyBTC >= 0.01)
                return Math.Round(howManyBTC, 2);
            else return 0.0;
        }

        /// <summary>
        /// A helper method used to determine how many BTC need to be sold to obtain a specified amount of dollars
        /// </summary>
        /// <param name="usdTarget">Target amount in USD</param>
        /// <param name="price">Price per BTC in USD</param>
        /// <param name="feeRate">Fee rate for account (%)</param>
        /// <returns></returns>
        public static double HowManyBTCDoINeedToGetThisUSD(double usdTarget, double price, double feeRate)
        {
            double howManyUSD = usdTarget / Math.Round(price * (1 - (feeRate / 100)), 5);
            return Math.Round(howManyUSD, 5);
        }

        /// <summary>
        /// Helper method to determine whether funds are sufficient to execute a trade
        /// </summary>
        /// <param name="currentBalanceUSD">Account balance USD</param>
        /// <param name="currentBalanceBTC">Account balance BTC</param>
        /// <param name="amount">Desired purchase amount</param>
        /// <param name="price">Desired purchase price</param>
        /// <param name="feeRate">Account fee rate</param>
        /// <param name="tradeType">Trade type bid/ask</param>
        /// <returns></returns>
        public static bool IsTradeFeasible(double currentBalanceUSD, double currentBalanceBTC, double amount, double price, double feeRate, MtGoxTradeType tradeType)
        {
            if (tradeType == MtGoxTradeType.ask)
            {
                if (currentBalanceBTC < amount)
                {
                    return false;
                }
            }
            if (tradeType == MtGoxTradeType.bid)
            {
                // make sure there is enough USD to allow the purchase
                if (currentBalanceUSD < (price * amount))
                {
                    return false;
                }
            }
            return true;
        }
    }
}
